<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>🌟 Vue 3 常见掉坑题（趣味抢答）</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <style>
    body {
      font-family: 'Arial', sans-serif;
      max-width: 1200px;
      margin: 0 auto;
      padding: 20px;
      background: linear-gradient(135deg, #ff6b6b 0%, #feca57 50%, #48dbfb 100%);
      min-height: 100vh;
    }
    .container {
      background: white;
      border-radius: 15px;
      padding: 30px;
      margin: 20px 0;
      box-shadow: 0 10px 30px rgba(0,0,0,0.2);
    }
    .title {
      color: #ff6b6b;
      text-align: center;
      font-size: 36px;
      margin-bottom: 30px;
      text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
      animation: bounce 2s infinite;
    }
    @keyframes bounce {
      0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
      40% { transform: translateY(-10px); }
      60% { transform: translateY(-5px); }
    }
    .quiz-container {
      background: #f8f9fa;
      border-radius: 15px;
      padding: 25px;
      margin: 20px 0;
      border: 3px solid #ff6b6b;
      position: relative;
      overflow: hidden;
    }
    .quiz-container::before {
      content: '❓';
      position: absolute;
      top: 10px;
      right: 15px;
      font-size: 24px;
      opacity: 0.3;
    }
    .question {
      font-size: 20px;
      font-weight: bold;
      color: #2d3748;
      margin-bottom: 20px;
      line-height: 1.6;
    }
    .options {
      display: grid;
      gap: 15px;
      margin: 20px 0;
    }
    .option {
      background: white;
      border: 2px solid #e2e8f0;
      border-radius: 10px;
      padding: 15px 20px;
      cursor: pointer;
      transition: all 0.3s ease;
      position: relative;
      overflow: hidden;
    }
    .option:hover {
      border-color: #ff6b6b;
      transform: translateY(-2px);
      box-shadow: 0 5px 15px rgba(0,0,0,0.1);
    }
    .option.selected {
      border-color: #ff6b6b;
      background: linear-gradient(135deg, #ff6b6b, #feca57);
      color: white;
      transform: scale(1.02);
    }
    .option.correct {
      border-color: #27ae60;
      background: linear-gradient(135deg, #27ae60, #2ecc71);
      color: white;
      animation: correctPulse 0.6s ease;
    }
    .option.incorrect {
      border-color: #e74c3c;
      background: linear-gradient(135deg, #e74c3c, #c0392b);
      color: white;
      animation: shake 0.5s ease;
    }
    @keyframes correctPulse {
      0% { transform: scale(1); }
      50% { transform: scale(1.05); }
      100% { transform: scale(1); }
    }
    @keyframes shake {
      0%, 100% { transform: translateX(0); }
      25% { transform: translateX(-5px); }
      75% { transform: translateX(5px); }
    }
    .explanation {
      background: #d1ecf1;
      border: 1px solid #bee5eb;
      border-radius: 8px;
      padding: 15px;
      margin: 15px 0;
      border-left: 4px solid #17a2b8;
      display: none;
      animation: slideDown 0.5s ease;
    }
    .explanation.show {
      display: block;
    }
    @keyframes slideDown {
      from { opacity: 0; transform: translateY(-10px); }
      to { opacity: 1; transform: translateY(0); }
    }
    .code-example {
      background: #2d3748;
      color: #e2e8f0;
      padding: 15px;
      border-radius: 8px;
      font-family: 'Courier New', monospace;
      margin: 10px 0;
      border-left: 4px solid #ff6b6b;
      overflow-x: auto;
    }
    .demo-area {
      background: white;
      border: 2px solid #ff6b6b;
      border-radius: 10px;
      padding: 20px;
      margin: 15px 0;
      box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    }
    .demo-controls {
      display: flex;
      gap: 15px;
      margin: 15px 0;
      flex-wrap: wrap;
    }
    .demo-output {
      background: #2d3748;
      color: #e2e8f0;
      padding: 15px;
      border-radius: 8px;
      font-family: 'Courier New', monospace;
      margin: 10px 0;
      min-height: 50px;
      border-left: 4px solid #ff6b6b;
    }
    .score-board {
      background: linear-gradient(135deg, #667eea, #764ba2);
      color: white;
      padding: 20px;
      border-radius: 15px;
      text-align: center;
      margin: 20px 0;
      box-shadow: 0 8px 16px rgba(0,0,0,0.2);
    }
    .score-item {
      display: inline-block;
      margin: 0 20px;
      padding: 10px 20px;
      background: rgba(255,255,255,0.2);
      border-radius: 20px;
      backdrop-filter: blur(10px);
    }
    .trap-card {
      background: linear-gradient(135deg, #ff9a9e, #fecfef);
      border-radius: 15px;
      padding: 20px;
      margin: 15px 0;
      border-left: 5px solid #ff6b6b;
      position: relative;
    }
    .trap-card::before {
      content: '⚠️';
      position: absolute;
      top: 15px;
      right: 15px;
      font-size: 20px;
    }
    .success-card {
      background: linear-gradient(135deg, #a8edea, #fed6e3);
      border-radius: 15px;
      padding: 20px;
      margin: 15px 0;
      border-left: 5px solid #27ae60;
      position: relative;
    }
    .success-card::before {
      content: '✅';
      position: absolute;
      top: 15px;
      right: 15px;
      font-size: 20px;
    }
    .warning-icon {
      color: #ff6b6b;
      font-size: 24px;
      margin-right: 10px;
    }
    .correct-icon {
      color: #27ae60;
      font-size: 24px;
      margin-right: 10px;
    }
    button {
      background: linear-gradient(135deg, #ff6b6b, #feca57);
      color: white;
      border: none;
      padding: 12px 24px;
      border-radius: 25px;
      cursor: pointer;
      font-size: 16px;
      font-weight: bold;
      transition: all 0.3s ease;
      box-shadow: 0 4px 8px rgba(0,0,0,0.2);
    }
    button:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 12px rgba(0,0,0,0.3);
    }
    button:disabled {
      opacity: 0.6;
      cursor: not-allowed;
      transform: none;
    }
    .reset-btn {
      background: linear-gradient(135deg, #667eea, #764ba2);
    }
    .next-btn {
      background: linear-gradient(135deg, #27ae60, #2ecc71);
    }
    .progress-bar {
      background: #e9ecef;
      border-radius: 10px;
      height: 8px;
      margin: 20px 0;
      overflow: hidden;
    }
    .progress-fill {
      background: linear-gradient(135deg, #ff6b6b, #feca57);
      height: 100%;
      border-radius: 10px;
      transition: width 0.5s ease;
    }
    .timer {
      font-size: 18px;
      font-weight: bold;
      color: #ff6b6b;
      text-align: center;
      margin: 10px 0;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <h1 class="title">🌟 Vue 3 常见掉坑题（趣味抢答）</h1>
      
      <!-- 计分板 -->
      <div class="score-board">
        <div class="score-item">
          <div>题目进度</div>
          <div style="font-size: 24px;">{{ currentQuestion + 1 }}/{{ questions.length }}</div>
        </div>
        <div class="score-item">
          <div>正确答题</div>
          <div style="font-size: 24px; color: #27ae60;">{{ correctAnswers }}</div>
        </div>
        <div class="score-item">
          <div>总得分</div>
          <div style="font-size: 24px; color: #feca57;">{{ totalScore }}分</div>
        </div>
      </div>

      <!-- 进度条 -->
      <div class="progress-bar">
        <div class="progress-fill" :style="{ width: progress + '%' }"></div>
      </div>

      <!-- 计时器 -->
      <div class="timer" v-if="timer > 0">
        ⏱️ 答题时间: {{ timer }}秒
      </div>

      <!-- 当前题目 -->
      <div class="quiz-container" v-if="currentQuestion < questions.length">
        <div class="question">
          <span class="warning-icon">🎯</span>
          {{ questions[currentQuestion].question }}
        </div>
        
        <div class="options">
          <div 
            v-for="(option, index) in questions[currentQuestion].options" 
            :key="index"
            class="option"
            :class="{
              'selected': selectedAnswer === index,
              'correct': showAnswer && index === questions[currentQuestion].correctAnswer,
              'incorrect': showAnswer && selectedAnswer === index && index !== questions[currentQuestion].correctAnswer
            }"
            @click="selectAnswer(index)"
            :style="{ animationDelay: index * 0.1 + 's' }"
          >
            <strong>{{ option.label }}.</strong> {{ option.text }}
          </div>
        </div>

        <!-- 答案解释 -->
        <div class="explanation" :class="{ 'show': showAnswer }">
          <h4><span class="correct-icon">💡</span> 答案解析</h4>
          <p>{{ questions[currentQuestion].explanation }}</p>
          
          <div class="code-example" v-if="questions[currentQuestion].code">
            <strong>代码示例：</strong>
            <pre>{{ questions[currentQuestion].code }}</pre>
          </div>
        </div>

        <!-- 控制按钮 -->
        <div style="text-align: center; margin-top: 20px;">
          <button 
            @click="submitAnswer" 
            :disabled="selectedAnswer === null || showAnswer"
            style="margin-right: 10px;"
          >
            {{ showAnswer ? '已提交' : '提交答案' }}
          </button>
          
          <button 
            @click="nextQuestion" 
            v-if="showAnswer"
            class="next-btn"
          >
            {{ currentQuestion < questions.length - 1 ? '下一题' : '查看结果' }}
          </button>
        </div>
      </div>

      <!-- 结果页面 -->
      <div v-else class="container">
        <div class="score-board">
          <h2>🎉 答题完成！</h2>
          <div style="font-size: 48px; margin: 20px 0;">
            {{ totalScore }}/{{ questions.length * 10 }}
          </div>
          <div style="font-size: 24px;">
            正确率：{{ Math.round((correctAnswers / questions.length) * 100) }}%
          </div>
        </div>

        <div class="success-card" v-if="correctAnswers >= questions.length * 0.8">
          <h3>🏆 恭喜！你是 Vue 3 响应式大师！</h3>
          <p>你已经掌握了 Vue 3 响应式的核心概念和常见陷阱，继续保持！</p>
        </div>

        <div class="trap-card" v-else>
          <h3>📚 继续加油！</h3>
          <p>还有一些知识点需要巩固，建议重新学习相关概念。</p>
        </div>

        <div style="text-align: center; margin-top: 30px;">
          <button @click="resetQuiz" class="reset-btn">重新开始</button>
        </div>
      </div>
    </div>

    <!-- 实战演示区域 -->
    <div class="container" v-if="currentQuestion < questions.length && questions[currentQuestion].demo">
      <div class="demo-area">
        <h3>🔧 实战演示：{{ questions[currentQuestion].demo.title }}</h3>
        <p>{{ questions[currentQuestion].demo.description }}</p>
        
        <div class="demo-controls">
          <button @click="runDemo">运行演示</button>
          <button @click="resetDemo" class="reset-btn">重置</button>
        </div>
        
        <div class="demo-output">
          <div v-if="demoResult">{{ demoResult }}</div>
          <div v-else style="opacity: 0.7;">点击"运行演示"查看效果...</div>
        </div>
      </div>
    </div>
  </div>

  <script>
    const { createApp, ref, reactive, toRefs, computed, watch } = Vue;

    createApp({
      setup() {
        // 题目数据
        const questions = ref([
          {
            question: "Q1: 为什么 const { count } = ref(0) 会丢失响应性？",
            options: [
              { label: "A", text: "解构的是基本值，脱离 ref 包裹" },
              { label: "B", text: "ref 不能解构使用" },
              { label: "C", text: "这是 Vue 3 的 bug" },
              { label: "D", text: "需要使用 reactive 而不是 ref" }
            ],
            correctAnswer: 0,
            explanation: "解构 ref 得到的是基本值，失去了响应性。正确做法是使用 toRefs 或者在 reactive 对象中解构。",
            code: `// ❌ 错误：丢失响应性
const { count } = ref(0)
count++ // 不会触发视图更新

// ✅ 正确：保持响应性
const state = reactive({ count: 0 })
const { count } = toRefs(state)
count.value++ // 会触发视图更新`,
            demo: {
              title: "解构响应性演示",
              description: "演示正确和错误的解构方式对响应性的影响"
            }
          },
          {
            question: "Q2: 直接给 reactive 数组 push 新对象，会响应吗？",
            options: [
              { label: "A", text: "会！reactive 数组方法被代理" },
              { label: "B", text: "不会，需要使用特殊方法" },
              { label: "C", text: "只有某些数组方法会响应" },
              { label: "D", text: "需要手动触发更新" }
            ],
            correctAnswer: 0,
            explanation: "reactive 会代理数组的所有方法，包括 push、pop、splice 等，所以直接 push 是完全响应的。",
            code: `// ✅ 正确：reactive 数组方法完全响应
const list = reactive([])
list.push({ id: 1, name: '项目1' }) // 完全响应
list.pop() // 完全响应
list.splice(0, 1) // 完全响应`,
            demo: {
              title: "数组响应性演示",
              description: "演示 reactive 数组的各种操作"
            }
          },
          {
            question: "Q3: 在定时器里修改 ref，视图会更新吗？",
            options: [
              { label: "A", text: "会！Vue3 代理不受异步限制" },
              { label: "B", text: "不会，需要使用 nextTick" },
              { label: "C", text: "只有某些情况下会更新" },
              { label: "D", text: "需要手动调用更新方法" }
            ],
            correctAnswer: 0,
            explanation: "Vue 3 的响应式系统基于 Proxy，不受异步限制，在定时器、Promise、async/await 中修改都会正常更新视图。",
            code: `// ✅ 正确：异步中修改也会响应
const count = ref(0)

setTimeout(() => {
  count.value++ // 视图会正常更新
}, 1000)

// 对比 Vue 2 的 $nextTick 需求
// Vue 3 不再需要这样的处理`,
            demo: {
              title: "异步更新演示",
              description: "演示定时器中的响应式更新"
            }
          },
          {
            question: "Q4: 以下哪种情况会导致响应式丢失？",
            options: [
              { label: "A", text: "重新赋值整个 reactive 对象" },
              { label: "B", text: "修改 reactive 对象的属性" },
              { label: "C", text: "使用数组的 push 方法" },
              { label: "D", text: "使用 computed 计算属性" }
            ],
            correctAnswer: 0,
            explanation: "重新赋值整个 reactive 对象会导致响应式丢失，因为新对象不再是响应式的。应该修改对象的属性而不是整个对象。",
            code: `// ❌ 错误：响应式丢失
const state = reactive({ count: 0 })
state = { count: 1 } // 整个对象被替换，失去响应性

// ✅ 正确：保持响应性
const state = reactive({ count: 0 })
state.count = 1 // 修改属性，保持响应性`,
            demo: {
              title: "对象替换演示",
              description: "演示重新赋值对响应性的影响"
            }
          },
          {
            question: "Q5: watch 和 watchEffect 的主要区别是什么？",
            options: [
              { label: "A", text: "watch 需要指定依赖，watchEffect 自动收集" },
              { label: "B", text: "watchEffect 性能更好" },
              { label: "C", text: "watch 只能监听一个值" },
              { label: "D", text: "watchEffect 不能访问旧值" }
            ],
            correctAnswer: 0,
            explanation: "watch 需要明确指定要监听的依赖，可以访问新值和旧值；watchEffect 会自动收集依赖，立即执行一次。",
            code: `// watch - 明确指定依赖
watch(count, (newVal, oldVal) => {
  console.log('count 变化:', oldVal, '->', newVal)
})

// watchEffect - 自动收集依赖
watchEffect(() => {
  console.log('count 当前值:', count.value)
  // 自动追踪 count.value 的变化
})`
          }
        ]);

        // 游戏状态
        const currentQuestion = ref(0);
        const selectedAnswer = ref(null);
        const showAnswer = ref(false);
        const correctAnswers = ref(0);
        const totalScore = ref(0);
        const timer = ref(0);
        const timerInterval = ref(null);
        const demoResult = ref('');

        // 计算属性
        const progress = computed(() => {
          return ((currentQuestion.value + 1) / questions.value.length) * 100;
        });

        // 方法
        const selectAnswer = (index) => {
          if (showAnswer.value) return;
          selectedAnswer.value = index;
        };

        const submitAnswer = () => {
          if (selectedAnswer.value === null) return;
          
          showAnswer.value = true;
          
          if (selectedAnswer.value === questions.value[currentQuestion.value].correctAnswer) {
            correctAnswers.value++;
            totalScore.value += 10;
          }
          
          stopTimer();
        };

        const nextQuestion = () => {
          currentQuestion.value++;
          selectedAnswer.value = null;
          showAnswer.value = false;
          demoResult.value = '';
          startTimer();
        };

        const resetQuiz = () => {
          currentQuestion.value = 0;
          selectedAnswer.value = null;
          showAnswer.value = false;
          correctAnswers.value = 0;
          totalScore.value = 0;
          demoResult.value = '';
          stopTimer();
          startTimer();
        };

        const startTimer = () => {
          timer.value = 30;
          timerInterval.value = setInterval(() => {
            timer.value--;
            if (timer.value <= 0) {
              if (!showAnswer.value) {
                submitAnswer();
              }
              stopTimer();
            }
          }, 1000);
        };

        const stopTimer = () => {
          if (timerInterval.value) {
            clearInterval(timerInterval.value);
            timerInterval.value = null;
          }
        };

        const runDemo = () => {
          const demo = questions.value[currentQuestion.value].demo;
          if (!demo) return;

          switch (currentQuestion.value) {
            case 0: // 解构响应性演示
              demoResult.value = '演示解构对响应性的影响...';
              setTimeout(() => {
                demoResult.value = '❌ 错误解构：const { count } = ref(0) 会丢失响应性\n✅ 正确解构：使用 toRefs 或在 reactive 中解构';
              }, 1000);
              break;
              
            case 1: // 数组响应性演示
              demoResult.value = '演示 reactive 数组操作...';
              setTimeout(() => {
                demoResult.value = '✅ reactive 数组的所有方法都是响应的！\npush() ✅ pop() ✅ splice() ✅ shift() ✅ unshift() ✅';
              }, 1000);
              break;
              
            case 2: // 异步更新演示
              demoResult.value = '演示定时器中的响应式更新...';
              let count = 0;
              const interval = setInterval(() => {
                count++;
                demoResult.value = `⏱️ 定时器执行第 ${count} 次，视图正常更新！`;
                if (count >= 3) {
                  clearInterval(interval);
                  demoResult.value += '\n🎉 Vue 3 的异步更新完全没问题！';
                }
              }, 1000);
              break;
              
            case 3: // 对象替换演示
              demoResult.value = '演示重新赋值对响应性的影响...';
              setTimeout(() => {
                demoResult.value = '❌ 重新赋值整个 reactive 对象会导致响应性丢失！\n✅ 应该修改对象的属性而不是整个对象。';
              }, 1000);
              break;
          }
        };

        const resetDemo = () => {
          demoResult.value = '';
        };

        // 初始化
        startTimer();

        return {
          questions,
          currentQuestion,
          selectedAnswer,
          showAnswer,
          correctAnswers,
          totalScore,
          timer,
          progress,
          demoResult,
          selectAnswer,
          submitAnswer,
          nextQuestion,
          resetQuiz,
          runDemo,
          resetDemo
        };
      }
    }).mount('#app');
  </script>
</body>
</html>